package fm.cheyiwang.controller;

import com.github.binarywang.wxpay.bean.WxPayOrderNotifyResponse;
import com.github.binarywang.wxpay.bean.request.WxPayBaseRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayBaseResult;
import com.github.binarywang.wxpay.bean.result.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.github.binarywang.wxpay.util.SignUtils;
import fm.config.MainConfig;
import fm.controller.BaseController;
import fm.dto.NeiyiUser;
import fm.entity.OrderRepair;
import fm.entity.OrderTransaction;
import fm.entityEnum.PayStatusEnum;
import fm.entityEnum.ProductTypeEnum;
import fm.entityEnum.RepairEnum;
import fm.exception.BizException;
import fm.yichenet.service.OrderService;
import fm.yichenet.service.RepairService;
import me.chanjar.weixin.common.exception.WxErrorException;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 维修控制器
 */
@Controller
@RequestMapping("/repair")
public class RepairController extends BaseController {

    @Autowired
    MainConfig mainConfig;

    private static Logger log = LoggerFactory.getLogger(RepairController.class);

    @Autowired
    WxPayService wxPayService;

    @Autowired
    RepairService repairService;

    @Autowired
    OrderService orderService;

    /**
     * 首页数据获取接口
     * 推荐热门技师的数据列表
     * 最近订单的数据接口
     *
     * @return
     */
    @ResponseBody
    @RequestMapping("/index")
    public Map index() {
        Map res = new HashMap();
        try {

            this.success(res);
        } catch (BizException ex) {
            LOGGER.error("occur business error", ex);
            this.failed(res, ex.getMessage());
        } catch (Exception ex) {
            LOGGER.error("occur un-expected error", ex);
            this.failed(res, "服务器发生未知错误，请稍后再试或者联系管理员解决！");
        }
        return res;
    }

    /**
     * 维修订单发布接口
     *
     * @param content   具体描述
     * @param title     标题
     * @param type      维修类型 FIXME 替换成枚举型
     * @param startTime 上门维修时间段 开始时间
     * @param endTime   上门维修时间段 结束时间
     * @param phone     联系手机、电话
     * @param address   具体地址 FIXME 请求中应该添加三个参数 province city district  省 市 区/县 加上这个详细地址address
     * @param loc       经纬度信息
     * @param pics      维修照片 FIXME 关联图片存mongo,订单数据存mysql，用id关联
     * @return
     */
    @ResponseBody
    @RequestMapping("/publish")
    public Map publish(@RequestParam String content, @RequestParam String title, @RequestParam String type,
                       String startTime, String endTime, @RequestParam String address, @RequestParam String province,
                       @RequestParam String city, @RequestParam String district, String pics,
                       @RequestParam String phone, @RequestParam String loc) {
        Map res = new HashMap();
        try {
            repairService.addPublish(content, title, type, startTime, endTime, address, province, city, district, pics, phone, loc);
            this.success(res);
        } catch (BizException ex) {
            LOGGER.error("occur business error", ex);
            this.failed(res, ex.getMessage());
        } catch (Exception ex) {
            LOGGER.error("occur un-expected error", ex);
            this.failed(res, "服务器发生未知错误，请稍后再试或者联系管理员解决！");
        }
        return res;
    }


    /**
     * 获取维修技师
     * dfrx、/自己的维修订单列表
     *
     * @param status   FIXME 订单的状态，这个要写一个枚举
     * @param type     维修类型 FIXME 这个要替换成枚举型
     * @param pageSize
     * @param pageNum
     * @return
     */
    @ResponseBody
    @RequestMapping("/myOrderList")
    public Map myOrderList(String status, String type,
                           @RequestParam(value = "pageSize", required = false, defaultValue = "20") Integer pageSize,
                           @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum) {
        Map res = new HashMap();
        try {
            NeiyiUser user = (NeiyiUser) getCurrentUser();
            List data = repairService.getEnginemenOrder(user, pageNum, pageSize, status, type);
            res.put("data", data);
            this.success(res);
        } catch (BizException ex) {
            LOGGER.error("occur business error", ex);
            this.failed(res, ex.getMessage());
        } catch (Exception ex) {
            LOGGER.error("occur un-expected error", ex);
            this.failed(res, "服务器发生未知错误，请稍后再试或者联系管理员解决！");
        }
        return res;
    }

    /**
     * 发布者订单
     * dfrx、/自己发布的维修订单列表
     *
     * @param status   FIXME 订单的状态，这个要写一个枚举
     * @param type     维修类型 FIXME 这个要替换成枚举型
     * @param pageSize
     * @param pageNum
     * @return
     */
    @ResponseBody
    @RequestMapping("/publishOrderList")
    public Map publishOrderList(String status, String type,
                                @RequestParam(value = "pageSize", required = false, defaultValue = "20") Integer pageSize,
                                @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum) {
        Map res = new HashMap();
        try {
            NeiyiUser user = (NeiyiUser) getCurrentUser();
            List data = repairService.getPublishOrder(user, pageNum, pageSize, status, type);
            res.put("data", data);
            this.success(res);
        } catch (BizException ex) {
            LOGGER.error("occur business error", ex);
            this.failed(res, ex.getMessage());
        } catch (Exception ex) {
            LOGGER.error("occur un-expected error", ex);
            this.failed(res, "服务器发生未知错误，请稍后再试或者联系管理员解决！");
        }
        return res;
    }


    /**
     * 维修订单获取接口
     *
     * @param province  city district      城市编码
     * @param type      维修类型
     * @param startTime 维修时间
     * @return
     */
    @ResponseBody
    @RequestMapping("/order/list")
    public Map repairOrderList(String province, String city, String district, String type, String startTime, Double lat, Double lon) {
        Map res = new HashMap();
        try {
            List list = repairService.getRepairListByTimeDistance(lat, lon, startTime, province, city, district, type);
            res.put("data", list);
            this.success(res);
        } catch (BizException ex) {
            LOGGER.error("occur business error", ex);
            this.failed(res, ex.getMessage());
        } catch (Exception ex) {
            LOGGER.error("occur un-expected error", ex);
            this.failed(res, "服务器发生未知错误，请稍后再试或者联系管理员解决！");
        }
        return res;
    }

    /**
     * 抢单接口
     * FIXME 接口线程sleep 3s后再返回  更新当前订单的维修技师ID字段、状态字段 ，
     * FIXME 操作需要加锁，操作需要加锁，操作需要加锁，可以再实体类上面添加version注解开启乐观锁
     *
     * @param orderId 订单ID
     * @return
     */
    @ResponseBody
    @RequestMapping("/order/contest")
    public Map contestOrder(String orderId) {
        Map res = new HashMap();
        try {
            NeiyiUser user = (NeiyiUser) getCurrentUser();
            repairService.updateCheckRepairOrder(user, orderId);
            OrderRepair orderRepair = repairService.getCheckRepairOrder(user, orderId);
            res.put("data", orderRepair);
            this.success(res);
        } catch (BizException ex) {
            LOGGER.error("occur business error", ex);
            this.failed(res, ex.getMessage());
        } catch (Exception ex) {
            LOGGER.error("occur un-expected error", ex);
            this.failed(res, "服务器发生未知错误，请稍后再试或者联系管理员解决！");
        }
        return res;
    }

    /**
     * 订单收款
     * TODO  更新订单状态、金额字段，更新后 用户方面可以发起支付
     * TODO  后台需要些一个jobs，检测订单支付状态，支付后更新维修技师的账户金额，增加该订单的收入，并且记录交易日志
     *
     * @param orderId 订单ID
     * @param amount  收款金额
     * @return
     */
    @ResponseBody
    @RequestMapping("/order/receipt")
    public Map receiptOrder(@RequestParam String orderId, @RequestParam String amount) {
        Map res = new HashMap();
        try {
            NeiyiUser user = (NeiyiUser) getCurrentUser();
            repairService.updateOrderRepairComplete(user, orderId, amount);
            this.success(res);
        } catch (BizException ex) {
            LOGGER.error("occur business error", ex);
            this.failed(res, ex.getMessage());
        } catch (Exception ex) {
            LOGGER.error("occur un-expected error", ex);
            this.failed(res, "服务器发生未知错误，请稍后再试或者联系管理员解决！");
        }
        return res;
    }


    /**
     * 订单评价
     * FIXME 评价数据存mongo
     *
     * @param orderId 订单Id
     * @param level   评价级别
     * @param content 文字内容
     * @param pics    图片数组
     * @return
     */
    @ResponseBody
    @RequestMapping("/order/comments")
    public Map commentOrder(String orderId, Integer level, String content, String pics) {
        Map res = new HashMap();
        try {
            NeiyiUser user = (NeiyiUser) getCurrentUser();
            repairService.commentsRepairOrder(user, orderId, level, content, pics);
            this.success(res);
        } catch (BizException ex) {
            LOGGER.error("occur business error", ex);
            this.failed(res, ex.getMessage());
        } catch (Exception ex) {
            LOGGER.error("occur un-expected error", ex);
            this.failed(res, "服务器发生未知错误，请稍后再试或者联系管理员解决！");
        }
        return res;
    }


    @ResponseBody
    @RequestMapping(value = "payorder")
    public Map payorder(String orderId, HttpServletRequest request) throws Exception {
        Map res = new HashedMap();
        OrderTransaction orderTransaction = null;
        try {
            orderTransaction = repairService.addOrderTransaction(orderId);
            NeiyiUser user = (NeiyiUser) getCurrentUser();
            WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
            orderRequest.setBody("车衣网订单中心");
            orderRequest.setOutTradeNo(orderTransaction.getId());
            log.info("user pay info OutTradeNo : {}", orderTransaction.getId());
            orderRequest.setTotalFee(WxPayBaseRequest.yuanToFee(String.valueOf(orderTransaction.getAmount())));//元转成分
            log.info("user pay info TotalFee : {}", WxPayBaseRequest.yuanToFee(String.valueOf(orderTransaction.getAmount())));
            orderRequest.setOpenid(user.getOpenid());
            log.info("user pay info openid : {}", user.getOpenid());
            orderRequest.setNotifyURL(mainConfig.getDomain() + "/repair/payresult");
            log.info("user pay info NotifyURL : {}", mainConfig.getDomain() + "/repair/payresult");
            orderRequest.setTradeType("JSAPI");

            String ip = request.getHeader("x-forwarded-for");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("PRoxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }

            log.info("user pay info real ip : {}", ip);

            if (ip.contains(",")) {
                ip = ip.split(",")[0];
            }
            orderRequest.setSpbillCreateIp(ip);

            Timestamp now = new Timestamp(System.currentTimeMillis());
            Timestamp expiredTime = new Timestamp(now.getTime() + (30 * 60 * 1000));
            DateFormat sdf1 = new SimpleDateFormat("yyyyMMddHHmmss");

            orderRequest.setTimeStart(sdf1.format(now));
            orderRequest.setTimeExpire(sdf1.format(expiredTime));
            Map data = wxPayService.getPayInfo(orderRequest);
            orderService.updateOrderTransactionStatus(orderTransaction.getId(), PayStatusEnum.PAYING, ProductTypeEnum.CUSTOM_SERVICE);
            //data.put("partenerId",mainConfig.getPartenerId());
            res.put("data", data);

            this.success(res);
        } catch (BizException e) {
            log.error("支付失败", e);
        } catch (WxErrorException e) {
            log.error("微信支付失败！订单号：{},原因:{}", orderTransaction.getId(), e);
            this.failed(res, "微信支付失败！订单号：" + orderTransaction.getId() + ",原因:" + e.getMessage());
        }
        return res;
    }

    @ResponseBody
    @RequestMapping(value = "apppayorder")
    public Map apppayorder(String orderId, HttpServletRequest request) throws Exception {
        Map res = new HashedMap();
        OrderTransaction orderTransaction = null;
        try {
            orderTransaction = repairService.addOrderTransaction(orderId);
            NeiyiUser user = (NeiyiUser) getCurrentUser();
            WxPayService appWxPayService = new WxPayServiceImpl();
            WxPayConfig wxPayConfig = new WxPayConfig();

            wxPayConfig.setAppId(mainConfig.getMobileAppid());
            wxPayConfig.setMchId(mainConfig.getMobilePartenerId());
            wxPayConfig.setMchKey(mainConfig.getMobilePartenerKey());
            wxPayConfig.setNotifyUrl(mainConfig.getDomain() + "/repair/apppayresult");
            wxPayConfig.setTradeType("APP");
            appWxPayService.setConfig(wxPayConfig);
            WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();

            orderRequest.setBody("车衣网订单中心");
            orderRequest.setOutTradeNo(orderTransaction.getId());
            log.info("user pay info OutTradeNo : {}", orderTransaction.getId());
            orderRequest.setTotalFee(WxPayBaseRequest.yuanToFee(String.valueOf(orderTransaction.getAmount())));//元转成分
            log.info("user pay info TotalFee : {}", WxPayBaseRequest.yuanToFee(String.valueOf(orderTransaction.getAmount())));
            String openid = user.getOpenid();

            orderRequest.setOpenid(openid);
            log.info("user pay info openid : {}", openid);
            orderRequest.setNotifyURL(mainConfig.getDomain() + "/repair/apppayresult");
            log.info("user pay info NotifyURL : {}", mainConfig.getDomain() + "/repair/apppayresult");
            orderRequest.setTradeType("APP");

            String ip = request.getHeader("x-forwarded-for");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("PRoxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
            orderRequest.setSpbillCreateIp(ip);
            log.info("user pay info real ip : {}", ip);
            Timestamp now = new Timestamp(System.currentTimeMillis());
            Timestamp expiredTime = new Timestamp(now.getTime() + (30 * 60 * 1000));
            DateFormat sdf1 = new SimpleDateFormat("yyyyMMddHHmmss");
            orderRequest.setTimeStart(sdf1.format(now));
            orderRequest.setTimeExpire(sdf1.format(expiredTime));


            Map data = appWxPayService.getPayInfo(orderRequest);
            Map<String, String> signParams = new HashedMap();
            signParams.put("appid", wxPayConfig.getAppId());
            signParams.put("partnerid", wxPayConfig.getMchId());
            signParams.put("prepayid", data.get("package").toString().split("=")[1]);
            signParams.put("package", "Sign=WXPay");
            signParams.put("noncestr", data.get("nonceStr").toString());
            signParams.put("timestamp", data.get("timeStamp").toString());
            String sign = SignUtils.createSign(signParams, wxPayConfig.getMchKey());
            signParams.put("sign", sign);

            orderService.updateOrderTransactionStatus(orderTransaction.getId(), PayStatusEnum.PAYING, ProductTypeEnum.CUSTOM_SERVICE);
            data.put("partenerId", mainConfig.getMobilePartenerId());
            res.put("data", signParams);

            this.success(res);
        } catch (BizException e) {
            this.failed(res, e);
        } catch (WxErrorException e) {
            log.error("微信支付失败！订单号：{},原因:{}", orderTransaction.getId(), e);
            this.failed(res, "微信支付失败！订单号：" + orderTransaction.getId() + ",原因:" + e.getMessage());
        }
        return res;
    }

    @ResponseBody
    @RequestMapping(value = "payresult")
    public String payNotify(HttpServletRequest request, HttpServletResponse response) {

        try {
            String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
            WxPayOrderNotifyResult result = wxPayService.getOrderNotifyResult(xmlResult);
            String orderId = result.getOutTradeNo();
            String tradeNo = result.getTransactionId();

            String totalFee = WxPayBaseResult.feeToYuan(result.getTotalFee());
            log.info("outTradeNo: {} ,tradeNo: {} ,real pay toalfee:{}", orderId, tradeNo, totalFee);
            orderService.updateOrderTransactionWxOrder(orderId, tradeNo, totalFee, ProductTypeEnum.CUSTOM_SERVICE);
            WxPayOrderQueryResult wxPayOrderQueryResult = wxPayService.queryOrder(null, orderId);
            if (wxPayOrderQueryResult.getTradeState().equals("SUCCESS")) {
                orderService.updateOrderTransactionStatus(orderId, PayStatusEnum.PAID,ProductTypeEnum.CUSTOM_SERVICE);
                log.info("user openid :{} ,pay succeed update OrderTransaction id {} ,wxOrderId: {}", result.getOpenid(), orderId, tradeNo);
                repairService.updateRepairStatus(orderId, RepairEnum.PAYMENT_SUCCESS);
                log.info("user openid :{} ,pay succeed update OrderRepair by OrderTransaction id {} ,wxOrderId: {}", result.getOpenid(), orderId, tradeNo);
            } else if (wxPayOrderQueryResult.getTradeState().equals("CLOSED") || wxPayOrderQueryResult.getTradeState().equals("PAYERROR")) {
                orderService.updateOrderTransactionStatus(orderId, PayStatusEnum.PAY_FAILED, ProductTypeEnum.CUSTOM_SERVICE);
                log.info("user openid :{} ,pay failed update OrderTransaction id {} ,wxOrderId: {}", result.getOpenid(), orderId, tradeNo);
            }
            //自己处理订单的业务逻辑，需要判断订单是否已经支付过，否则可能会重复调用
            return WxPayOrderNotifyResponse.success("处理成功!");
        } catch (BizException ex) {
            log.info("repair/payresult info BizException msg : {} ", ex);
            return WxPayOrderNotifyResponse.success("处理成功!");
        } catch (Exception e) {
            log.error("微信回调结果异常,异常原因{}", e);
            return WxPayOrderNotifyResponse.fail(e.getMessage());
        }
    }


    /**
     * TODO 这里应该是接受回调后主动向微信服务器发起一次查询
     *
     * @param request
     * @param response
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "apppayresult")
    public String appPayNotify(HttpServletRequest request, HttpServletResponse response) {

        try {
            WxPayService appWxPayService = new WxPayServiceImpl();
            WxPayConfig wxPayConfig = new WxPayConfig();
            wxPayConfig.setAppId(mainConfig.getMobileAppid());
            wxPayConfig.setMchId(mainConfig.getMobilePartenerId());
            wxPayConfig.setMchKey(mainConfig.getMobilePartenerKey());
            wxPayConfig.setNotifyUrl(mainConfig.getDomain() + "/repair/apppayresult");
            wxPayConfig.setTradeType("APP");
            appWxPayService.setConfig(wxPayConfig);

            String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
            log.info("微信APP支付回调信息:{}", xmlResult);

            WxPayOrderNotifyResult result = appWxPayService.getOrderNotifyResult(xmlResult);
            String orderId = result.getOutTradeNo();
            String tradeNo = result.getTransactionId();
            String totalFee = WxPayBaseResult.feeToYuan(result.getTotalFee());

            //主动查询微信服务器
            log.info("outTradeNo: {} ,tradeNo: {} ,real pay toalfee:{}", orderId, tradeNo, totalFee);
            orderService.updateOrderTransactionWxOrder(orderId, tradeNo, totalFee, ProductTypeEnum.CUSTOM_SERVICE);
            WxPayOrderQueryResult wxPayOrderQueryResult = appWxPayService.queryOrder(null, orderId);

            if (wxPayOrderQueryResult.getTradeState().equals("SUCCESS")) {
                orderService.updateOrderTransactionStatus(orderId, PayStatusEnum.PAID, ProductTypeEnum.CUSTOM_SERVICE);
                log.info("user openid :{} ,pay succeed update OrderTransaction id {} ,wxOrderId: {}", result.getOpenid(), orderId, tradeNo);

                repairService.updateRepairStatus(orderId, RepairEnum.PAYMENT_SUCCESS);
                log.info("user openid :{} ,pay succeed update OrderRepair by OrderTransaction id {} ,wxOrderId: {}", result.getOpenid(), orderId, tradeNo);

            } else if (wxPayOrderQueryResult.getTradeState().equals("CLOSED") || wxPayOrderQueryResult.getTradeState().equals("PAYERROR")) {

                orderService.updateOrderTransactionStatus(orderId, PayStatusEnum.PAY_FAILED, ProductTypeEnum.CUSTOM_SERVICE);
                log.info("user openid :{} ,pay failed update OrderTransaction id {} ,wxOrderId: {}", result.getOpenid(), orderId, tradeNo);

            }

            //自己处理订单的业务逻辑，需要判断订单是否已经支付过，否则可能会重复调用
            return WxPayOrderNotifyResponse.success("处理成功!");

        } catch (BizException ex) {
            log.info("repair/payresult info BizException msg : {} ", ex);
            return WxPayOrderNotifyResponse.success("处理成功!");
        } catch (Exception e) {
            log.error("微信回调结果异常,异常原因{}", e);
            return WxPayOrderNotifyResponse.fail(e.getMessage());
        }
    }


}
